#pragma once

#include "common.h"

/**
 * The tokenizer will tokenize the given file and provide as much segmented
 * information and details message as possible regarding the tokenization process.
 * The tokenizer will create XML element containing the token information.
 * Token schema:
 *
 * <token type="TYPE" value="STRING" line="INT" column="INT"/>
 *
 * Token types are:
 * 		type = KEYWORD|IDENTIFIER|INTEGER|FLOAT|CHARACTER|STRING|PUNCTUATOR|COMMENT|UNSET
 *
 * Keyword values are:
 * 		value = auto|enum|restrict|unsigned|break|extern|return|voice|case|float|short|volatile|char|for|signed|while|const|goto|sizeof|_bool|continue|if|static|_complex|default|inline|struct|imaginary|double|long|typedef|else|register|union
 *
 * Integer and float attributes are:
 * 		integerType = DECIMAL|HEX|OCTAL
 * 		integerSuffic = UNSIGNED|LONG|LONGLONG|UNSIGNEDLONG|UNSIGNEDLONGLONG
 * 		floatSuffix = FLOATING, FLOATLONG
 *
 * Other attributes for floats and integers are the components of the token which are:
 * 		decimal = INTEGER
 * 		fraction = INTEGER
 * 		exponent = INTEGER
 *
 * These attributes will contain the string value along with the converted integer equivalent.
 */
typedef struct Lexer
{
	char* file;		//Name of file that this lexer is parsing. Used to identify the file name when an error occurs.
	Reader* reader;	//Points to the reader containing the file buffered data.

	XmlElement* token;
	XmlDocument* tokens;
	XmlDocument* messages;
}Lexer;

void LexerFree(Lexer* pLexer);
Lexer* LexerCreate(const char* sFilePath);

int32 LexerNext(Lexer* pLexer);
int32 LexerReadComment(Lexer* pLexer);
int32 LexerReadCommentSimple(Lexer* pLexer);
int32 LexerReadIdentifier(Lexer* pLexer);
int32 LexerReadCharConstant(Lexer* pLexer);
int32 LexerReadStringLiteral(Lexer* pLexer);
int32 LexerReadStringOrChar(Lexer* pLexer, const int32 iQuote);
int32 LexerTryPunctuator(Lexer* pLexer);
int32 LexerReadSimpleEscapeSequence(Lexer* pLexer);
int32 LexerReadOctalEscapeSequance(Lexer* pLexer);
int32 LexerReadHexEscapeSequence(Lexer* pLexer);
int32 LexerReadDecimalConstant(Lexer* pLexer);
int32 LexerReadOctalConstant(Lexer* pLexer);
int32 LexerReadHexConstant(Lexer* pLexer);
int32 LexerReadHexFloatingConstant(Lexer* pLexer);
int32 LexerTryIntegerSuffix(Lexer* pLexer);
int32 LexerReadDecimalFloatingConstant(Lexer* pLexer);
int32 LexerReadExponentPart(Lexer* pLexer);
int32 LexerReadHexSequence(Lexer* pLexer, String* sString);
int32 LexerReadDigitSequence(Lexer* pLexer, String* sString);

int32 LexerIsKeyword(int32 iHashcode);
int32 LexerReadUniversalCharName(Lexer* pLexer);
int32 LexerReadHexQuad(Lexer* pLexer, int32 iCount);
int32 LexerSkipWhiteSpace(Lexer* pLexer);

#define LexerIsNonDigit(iChar) ((iChar) == '_' || ((iChar) >= 'a' && (iChar) <= 'z') || ((iChar) >= 'A' && (iChar) <= 'Z'))
#define LexerIsWhiteSpace(iChar) ((iChar) == ' ' || (iChar) == '\v' || (iChar) == '\n' || (iChar) == '\r' || (iChar) == '\t' || (iChar) == '\v')
#define LexerIsUniversalCharName(iChar1, iChar2) ((iChar1) == '\\' && ((iChar2) == 'u' || (iChar2) == 'U'))
#define LexerIsCharConstant(iChar1, iChar2) ((iChar1) == '\'' || ((iChar1) == 'L' && (iChar2) == '\''))
#define LexerIsStringLiteral(iChar1, iChar2) ((iChar1) == '"' || ((iChar1) == 'L' && (iChar2) == '"'))
#define LexerIsCharOrStringIdentiferStart(iChar1, iChar2) ((iChar1) == 'L' && ((iChar2) == '"' || (iChar2) == '\''))
#define LexerIsDigit(iChar) ((iChar) >= '0' && (iChar) <= '9')
#define LexerIsComment(iChar1, iChar2) ((iChar1) == '/' && (iChar2) == '*')
#define LexerIsCommentSimple(iChar1, iChar2) ((iChar1) == '/' && (iChar2) == '/')
#define LexerIsHexPrefix(iChar1, iChar2) ((iChar1) == '0' && ((iChar2) == 'x' || (iChar2) == 'X'))
#define LexerIsNonZeroDigit(iChar) ((iChar) >= '1' && (iChar) <= '9')
#define LexerIsOctalDigit(iChar) ((iChar) >= '0' && (iChar) <= '7')
#define LexerIsHexDigit(iChar) (((iChar) >= '0' && (iChar) <= '9') || ((iChar) >= 'a' && (iChar) <='f') || ((iChar) >= 'A' && (iChar) <= 'F'))
#define LexerIsOctalStart(iChar) ((iChar) == '0')
#define LexerIsBinaryExponent(iChar) ((iChar) == 'p' || (iChar) == 'P')
#define LexerIsFloatingSuffix(iChar) ((iChar) == 'f' || (iChar) == 'F' || (iChar) == 'l' || (iChar) == 'L')
#define LexerIsSign(iChar) ((iChar) == '+' || (iChar) == '-')
#define LexerIsExponent(iChar) ((iChar) == 'e' || (iChar) == 'E')
#define LexerIsUnsignedSuffix(iChar) ((iChar) == 'u' || (iChar) == 'U')
#define LexerIsLongSuffix(iChar) ((iChar) == 'l' || (iChar) == 'L')
#define LexerIsLongLongSuffix(iChar1, iChar2) (((iChar1) == 'l' && (iChar2) == 'l') || ((iChar1) == 'L' && (iChar2) == 'L'))
#define LexerIsDecimalFloatingConstant(iChar1, iChar2) ((iChar1) == '.' && LexerIsDigit(iChar2))
#define LexerOctDigitToInt(iChar) ((iChar) - '0')
#define LexerHexDigitToInt(iChar) ( ((iChar) >= '0' && (iChar) <= '8') ? (iChar) - '0' : ( ((iChar) >= 'A' && (iChar) <= 'Z') ? (iChar) - 'A' + 10 : (iChar) - 'a' + 10 ) );

#define KW_AUTO			0x002DDDAF
#define KW_ENUM			0x002F9501
#define KW_RESTRICT		0xEBF0BABC
#define KW_UNSIGNED		0xFF0C66F5
#define KW_BREAK		0x059A58FF
#define KW_EXTERN		0xB32ABFA0
#define KW_RETURN		0xC84E3D30
#define KW_VOID			0x00375194
#define KW_CASE			0x002E7B30
#define KW_FLOAT		0x05D0225C
#define KW_SHORT		0x0685847C
#define KW_VOLATILE		0x8F76FD9C
#define KW_CHAR			0x002E9356
#define KW_FOR			0x00018CC9
#define KW_SIGNED		0xCA356F1C
#define KW_WHILE		0x06BDCB31
#define KW_CONST		0x05A73763
#define KW_GOTO			0x00308163
#define KW_SIZEOF		0xCA3DF198
#define KW__BOOL		0x055A67E9
#define KW_CONTINUE		0xDE312CA7
#define KW_IF			0x00000D1D
#define KW_STATIC		0xCACDCE6E
#define KW__COMPLEX		0x286FA351
#define KW_DEFAULT		0x5C13D641
#define KW_INLINE		0xB96DA299
#define KW_STRUCT		0xCAD58BD5
#define KW_IMAGINARY	0x435E7CF9
#define KW_DOUBLE		0xB0F77BD1
#define KW_LONG			0x0032C67C
#define KW_TYPEDEF		0xCD26F28B
#define KW_ELSE			0x002F8D39
#define KW_REGISTER		0xD6DC2EA3
#define KW_UNION		0x06A456CF

#define LBRAKET			0x0000005B /*[*/
#define RBRAKET			0x0000005D /*]*/
#define LPARAN			0x00000028 /*(*/
#define RPARAN			0x00000029 /*)*/
#define LCBRAKET		0x0000007B /*{*/
#define RCBRAKET		0x0000007D /*}*/
#define PERIOD			0x0000002E /*.*/
#define MEMBER			0x000005B1 /*->*/
#define INCROMENT		0x00000560 /*++*/
#define DECREMENT		0x000005A0 /*--*/
#define AND				0x00000026 /*&*/
#define ASTERISK		0x0000002A /***/
#define ADD				0x0000002B /*+*/
#define SUBTRACT		0x0000002D /*-*/
#define COMPLEMENT		0x0000007E /*~*/
#define NOT				0x00000021 /*!*/
#define DIVIDE			0x0000002F /*/*/
#define MODULUS			0x00000025 /*%*/
#define LSHIFT			0x00000780 /*<<*/
#define RSHIFT			0x000007C0 /*>>*/
#define LESS			0x0000003C /*<*/
#define GREATER			0x0000003E /*>*/
#define LESSEQUAL		0x00000781 /*<=*/
#define GREATEREQUAL	0x000007BF /*>=*/
#define EQUAL			0x000007A0 /*==*/
#define NOTEQUAL		0x0000043C /*!=*/
#define XOR				0x0000005E /*^*/
#define OR				0x0000007C /*|*/
#define LOGICAND		0x000004C0 /*&&*/
#define LOGICOR			0x00000F80 /*||*/
#define QUESTION		0x0000003F /*?*/
#define COLON			0x0000003A /*:*/
#define SEMICOLON		0x0000003B /*;*/
#define DOTS			0x0000B26E /*...*/
#define ASSIGN			0x0000003D /*=*/
#define MULASSIGN		0x00000553 /**=*/
#define DIVASSIGN		0x000005EE /*/=*/
#define MODASSIGN		0x000004B8 /*%=*/
#define ADDASSIGN		0x00000572 /*+=*/
#define SUBASSIGN		0x000005B0 /*-=*/
#define LSHIFTASSGIN	0x0000E8BD /*<<=*/
#define RSHIFTASSGIN	0x0000F07D /*>>=*/
#define ANDASSIGN		0x000004D7 /*&=*/
#define XORASSIGN		0x00000B9F /*^=*/
#define ORASSIGN		0x00000F41 /*|=*/
#define COMMA			0x0000002C /*,*/
#define HASH			0x00000023 /*#*/
#define HASHHASH		0x00000460 /*##*/
#define _LBRAKET		0x0000077E /*<:*/
#define _RBRAKET		0x00000744 /*:>*/
#define _LCBRAKET		0x00000769 /*<%*/
#define _RCBRAKET		0x000004B9 /*%>*/
#define _HASH			0x000004B5 /*%:*/
#define _HASHHASH		0x0011B02A /*%:%:*/
